#pragma once

#include <iostream>
#include <queue>
#include <pthread.h>

namespace BlockQueueModule
{
    static int g_queue_capacity = 10;
    
    template<class T>
    class BlockQueue{
    public:
        int isFull() { return _queue.size() == _cap; }
        int isEmpty() { return _queue.size() == 0; }
    public:

        BlockQueue()
            : _cond_producer_num(0),_cond_consumer_num(0)
        {
            pthread_mutex_init(&_mutex, nullptr);
            pthread_cond_init(&_consumer_cond, nullptr);
            pthread_cond_init(&_producer_cond, nullptr);
        }
        ~BlockQueue()
        {
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_consumer_cond);
            pthread_cond_destroy(&_producer_cond);
        }

        // 生产者
        void Enqueue(const T &in)
        {
            pthread_mutex_lock(&_mutex);
            // 生产一个数据
            
            // 对条件进行判断，为了防止伪唤醒，我们通常使用while进行判断！
            while(isFull()) //用 while 防止 spurious wakeup问题
            {
                ++_cond_producer_num;
                pthread_cond_wait(&_producer_cond, &_mutex);
                --_cond_producer_num;
            }
            _queue.push(in);
            
            // 我们是持有锁的，push了队列肯定有数据
            if(_cond_consumer_num)
            {
                pthread_cond_signal(&_consumer_cond);
                // usleep(100000);
            }
            // 归还锁
            pthread_mutex_unlock(&_mutex);
        }

        // 消费者
        void Dequeue(T *out)
        {
            pthread_mutex_lock(&_mutex);

            // 对条件进行判断，为了防止伪唤醒，我们通常使用while进行判断！
            while(isEmpty())
            {
                ++_cond_consumer_num;
                pthread_cond_wait(&_consumer_cond, &_mutex); // 等待的时候要归还锁，所以需要传 _mutex
                --_cond_consumer_num;
            }
            *out = _queue.front();
            _queue.pop();

            // pop了 一定有空间
            if(_cond_producer_num)
            {
                pthread_cond_signal(&_producer_cond);
                // usleep(100000);
            }
            pthread_mutex_unlock(&_mutex);
        }
    private:
        std::queue<T> _queue;
        pthread_mutex_t _mutex;

        pthread_cond_t _producer_cond;
        pthread_cond_t _consumer_cond;

        int _cond_consumer_num;
        int _cond_producer_num;

        int _cap = g_queue_capacity;
    };


}